昨天,我們認識了角色,鴨子類別是一種 角色 ,能夠表現共同行為的角色都算是鴨子類別。再來,我們可以定義一組獨立於類別的方法,並且可以被其他物件使用,稱為 模組 (module) 。
今天,我們要接著來看模組內的方法是如何自動委派給其他物件使用,他與經典繼承又有什麼不同。
當物件接收到訊息時,OO語言首先會在該物件的類別裡尋找符合的方法實作,如果類別沒有實作該訊息,則繼續搜尋它的父類別。這個搜尋會沿著父類別鏈繼續向上,並在一層層的父類別裡尋找,直到層次結構的頂端。
注意在Bicycle
和Object
之間突顯了一個Schedulable
模組。
Bicycle
類別外掛了Schedulable
這個模組時,其定義的所有方法都會成為Bicycle
的可回應方法Bicycle
的父類別不會因為包含這個模組而有所變化(仍然是object)Bicycle
實作了某個方法,它在Schedulable
裡也有被定義, 那麼Bicycle
的實作將會覆蓋Schedulable
裡的相同方法。include
和extend
:
include
關鍵字將模組包括到類別中,將模組方法添加到該類別的所有實例的可回extend
關鍵字將模組添加到特定物件中,將模組方法添加到該物件的可回方法中。任何物件都可以擁有自己的單例類別,並在其中直接添加特定方法,這些方法專屬於該物件。
方法定義的位置影響方法的可回應集,使用不同的方法來擴展物件的可回應集,可以將方法定義放置在不同的位置,如類別、模組、實例,或單例類別。
物件使用type
或category
這類名稱的變數來確認傳送給self
的是何種訊息,該訊息包含兩種極為相關卻又有所不同的類型。像這樣的程式碼可以使用經典繼承,將共同的程式碼放置在某個抽象父類別裡,並為各個不同的類型建立子類別。
傳送物件要檢查接收物件的類別以確認所應傳送的訊息,那麼便忽略了鴨子類型的存在。在這種情況下,所有可能的接收物件都在扮演某個共同角色。這個角色應該被程式碼撰寫為一種鴨子類型,而接收者則應該實作這個鴨子類型的介面
抽象父類別裡的所有程式碼都應該適用於每一個繼承它的類別。父類別不應該包含只適用於部分(而非全部)子類別的程式碼。
子類別都同意這樣的契約:它們都承諾能夠代替其父類別。只有當物件如預期般表現,並且子類別也合乎預期的遵從其父類別介面,才可能具有可代替性。它們都必須回應該介面裡的每一則訊息,接收相同類型的輸入,以及傳回相同類型的輸出。其他物件應當不需要對其進行類型檢查,就能夠知道該如何對待或是期望它們。
用於建立可繼承程式碼的基本撰寫技巧是使用範本方法模式,這種模式可以讓你將抽象與具體分離開來。
請避免撰寫需要繼承者傳送super
的程式碼。可以藉由鉤子訊息讓子類別參與進來,同時還可免除它們需要知道抽象演算法的職責。
每一個層次結構都可以想成是一座金字塔,它有深度和廣度。物件的深度是指在它和頂端之間所有父類別的數量,而廣度則指的是它的直接子類別數量。
深窄的層次結構有著更高的難度,並且很不幸地,它們的寬度其實會逐漸增加,這是出於其深度所造成的副作用。深寬的層次結構難以理解與維護,應該要加以避免。
super
(導致它們知悉演算法)。參考資料: